package com.fary.http.converter;

import com.fary.http.HttpInputMessage;
import com.fary.http.HttpOutputMessage;
import com.fary.http.MediaType;
import com.fary.util.Assert;
import com.fary.util.StreamUtils;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {

    /**
     * 默认字符集(产生乱码的根源)
     */
    public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;

    // 可使用的字符集
    private volatile List<Charset> availableCharsets;

    //标识是否输出 Response Headers:Accept-Charset(默认输出)
    private boolean writeAcceptCharset = true;

    /**
     * 使用 "ISO-8859-1" 作为默认字符集的默认构造函数
     */
    public StringHttpMessageConverter() {
        this(DEFAULT_CHARSET);
    }


    /**
     * 如果请求的内容类型 Content-Type 没有指定一个字符集，则使用构造函数提供的默认字符集
     */
    public StringHttpMessageConverter(Charset defaultCharset) {
        super(defaultCharset, MediaType.TEXT_PLAIN, MediaType.ALL);
    }


    /**
     * 标识是否输出 Response Headers:Accept-Charset
     * 默认是 true
     */
    public void setWriteAcceptCharset(boolean writeAcceptCharset) {
        this.writeAcceptCharset = writeAcceptCharset;
    }


    @Override
    public boolean supports(Class<?> clazz) {
        return String.class == clazz;
    }

    /**
     * 将请求报文转换为字符串
     */
    @Override
    protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
        // 通过读取请求报文里的 Content-Type 来获取字符集
        Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
        // 调用 StreamUtils 工具类的 copyToString 方法来完成转换
        return StreamUtils.copyToString(inputMessage.getBody(), charset);
    }

    @Override
    protected Long getContentLength(String str, MediaType contentType) {
        Charset charset = getContentTypeCharset(contentType);
        return (long) str.getBytes(charset).length;
    }

    /**
     * 将字符串转换为响应报文
     */
    @Override
    protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
        //输出 Response Headers:Accept-Charset(默认输出)
        if (this.writeAcceptCharset) {
            outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
        }
        Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
        //调用 StreamUtils 工具类的 copy 方法来完成转换
        StreamUtils.copy(str, charset, outputMessage.getBody());
    }


    /**
     * 返回所支持的字符集
     * 默认返回 Charset.availableCharsets()
     * 子类可以覆盖该方法
     */
    protected List<Charset> getAcceptedCharsets() {
        List<Charset> charsets = this.availableCharsets;
        if (charsets == null) {
            charsets = new ArrayList<>(Charset.availableCharsets().values());
            this.availableCharsets = charsets;
        }
        return charsets;
    }

    /**
     * 获得 ContentType 对应的字符集
     */
    private Charset getContentTypeCharset(MediaType contentType) {
        if (contentType != null && contentType.getCharset() != null) {
            return contentType.getCharset();
        } else if (contentType != null && contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) {
            // Matching to AbstractJackson2HttpMessageConverter#DEFAULT_CHARSET
            return StandardCharsets.UTF_8;
        } else {
            Charset charset = getDefaultCharset();
            Assert.state(charset != null, "No default charset");
            return charset;
        }
    }

}